package com.ds.lens.data.service.metric;

import com.ds.lens.data.common.asserts.Asserts;
import com.ds.lens.data.common.client.vo.LensMetricInVO;
import com.ds.lens.data.common.constant.AggregatorEnum;
import com.ds.lens.data.common.constant.Constants;
import com.ds.lens.data.common.query.MQLVariable;
import com.ds.lens.data.common.umpapi.UmpMetricClient;
import com.ds.lens.data.common.umpapi.UmpMetricOutVO;
import com.ds.lens.data.common.umpapi.VectorMetric;
import com.ds.lens.data.common.umpapi.VectorMetricOutBO;
import com.ds.lens.data.common.util.HttpServiceClient;
import com.ds.lens.data.common.util.LensMQLBuilder;
import com.ds.lens.data.common.util.MapUtils;
import com.ds.lens.data.common.util.StartEndTimeRange;
import com.ds.lens.data.service.dao.LensIntegrationDao;
import com.ds.lens.data.service.dao.entity.LensIntegration;
import com.ds.lens.data.vo.out.v2.integration.LensIntegrationDetailOutVO;
import com.ds.lens.data.vo.out.v2.metric.LensMetricNamesOutVO;
import com.ds.lens.data.vo.out.v2.monitor.LensMonitorFilterOutVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.*;

/**
 * Description:
 *
 * @author WeiShaoying
 * @date 2019-12-24
 */
@Slf4j
@Service(Constants.METRIC_SERVICE_PREFIX + Constants.UMP)
public class UmpMetricQueryServiceImpl implements MetricQueryService {

    private static final String UMP_INTEGRATION_NAME = Constants.UMP;
    private UmpQueryAdaptor umpQueryAdaptor;
    @Autowired
    private LensIntegrationDao lensIntegrationDao;

    @PostConstruct
    public void init() {
        try {
            LensIntegration lensIntegration = lensIntegrationDao.selectByName(UMP_INTEGRATION_NAME);

            umpQueryAdaptor = new UmpQueryAdaptor(lensIntegration.getUrl());
            umpQueryAdaptor.connect();
        } catch (Exception e) {
            log.error("An error occurred.", e);
        }
    }

    @PreDestroy
    public void cleanup() {
        try {
            if (umpQueryAdaptor != null) {
                umpQueryAdaptor.close();
            }
        } catch (Exception e) {
            log.error("An error occurred.", e);
        }
    }

    @Override
    public List<Map<String, Object>> query(LensMetricInVO lensMetricInVO, LensIntegrationDetailOutVO integrationDetailOutVO, MQLVariable... mqlVariables) {
        String sql = LensMQLBuilder.buildQueryDSL(lensMetricInVO.getMetricName(),
                AggregatorEnum.valueOf(lensMetricInVO.getMetricAggr()).name(),
                integrationDetailOutVO.getName(),
                MapUtils.merge(new HashMap<>(), lensMetricInVO.getScope(), lensMetricInVO.getMetricFilter()),
                null,
                lensMetricInVO.getMetricBy(),
                new StartEndTimeRange(lensMetricInVO.getStartTime(), lensMetricInVO.getEndTime()),
                lensMetricInVO.getInterval(),
                lensMetricInVO.getGroupLimit());
        try {
            return umpQueryAdaptor.query(sql, mqlVariables);
        } catch (Exception e) {
            log.error("An error occurred. while query sql: " + sql, e);
            return null;
        }
    }

    @Override
    public List<String> getGroupByFields(LensIntegrationDetailOutVO integrationDetailOutVO, Long applicationId, String metricName) {
        Asserts.assertNotNull(integrationDetailOutVO, Constants.ErrorCode.NO_INTEGRATION_EXCEPTION);
        UmpMetricClient umpMetricClient = HttpServiceClient.getRestClient(UmpMetricClient.class, integrationDetailOutVO.getUrl());
        UmpMetricOutVO<VectorMetricOutBO> metricOutVO;
        try {
            metricOutVO = umpMetricClient.getCurrentMetrics(metricName);
            Asserts.assertNotNull(metricOutVO, Constants.ErrorCode.METRIC_VALUE_IS_NULL);
            Set<String> set = new HashSet<>();
            if (null != metricOutVO.getData() && null != metricOutVO.getData().getResult()) {
                for (VectorMetric vectorMetric : metricOutVO.getData().getResult()) {
                    set.addAll(vectorMetric.getMetric().keySet());
                }
            }
            set.remove("__name__");
            return new ArrayList<>(set);
        } catch (Exception e) {
            log.error("UMP interface error.Method:getGroupByFields", e);
        }
        return Collections.emptyList();
    }

    @Override
    @Cacheable(value = "integrationMetricNames", key = "#integrationDetailOutVO.id + #integrationDetailOutVO.name", sync = true)
    public List<LensMetricNamesOutVO> getMetricNames(LensIntegrationDetailOutVO integrationDetailOutVO, long applicationId) {
        List<LensMetricNamesOutVO> result = new ArrayList<>();
        Asserts.assertNotNull(integrationDetailOutVO, Constants.ErrorCode.NO_INTEGRATION_EXCEPTION);
        UmpMetricClient umpMetricClient = HttpServiceClient.getRestClient(UmpMetricClient.class, integrationDetailOutVO.getUrl());
        try {
            UmpMetricOutVO<List<String>> outVO = umpMetricClient.getMetricNames();
            Asserts.assertNotNull(outVO, Constants.ErrorCode.NO_METRIC_NAMES);
            if (!CollectionUtils.isEmpty(outVO.getData())) {
                LensMetricNamesOutVO metricNamesOutVO;
                for (String str : outVO.getData()) {
                    metricNamesOutVO = new LensMetricNamesOutVO();
                    metricNamesOutVO.setName(str);
                    result.add(metricNamesOutVO);
                }
            }
        } catch (Exception e) {
            log.error("UMP interface error.Method:getMetricNames", e);
        }
        return result;
    }

    @Override
    public List<LensMonitorFilterOutVO> getFilters(LensIntegrationDetailOutVO integrationDetailOutVO, Long applicationId, String tierId, String metricName) {
        List<LensMonitorFilterOutVO> result = new ArrayList<>();
        Asserts.assertNotNull(integrationDetailOutVO, Constants.ErrorCode.NO_INTEGRATION_EXCEPTION);
        UmpMetricClient umpMetricClient = HttpServiceClient.getRestClient(UmpMetricClient.class, integrationDetailOutVO.getUrl());
        try {
            Asserts.assertNotEmpty(metricName, Constants.ErrorCode.NO_METRIC_NAMES);
            UmpMetricOutVO<VectorMetricOutBO> metricOutVO = umpMetricClient.getCurrentMetrics(metricName);
            Asserts.assertNotNull(metricOutVO, Constants.ErrorCode.METRIC_VALUE_IS_NULL);
            LensMonitorFilterOutVO outVO;
            if (null != metricOutVO.getData() && null != metricOutVO.getData().getResult()) {
                for (VectorMetric vectorMetric : metricOutVO.getData().getResult()) {
                    for (Map.Entry<String, String> entry : vectorMetric.getMetric().entrySet()) {
                        if ("__name__".equals(entry.getKey())) {
                            continue;
                        }
                        outVO = new LensMonitorFilterOutVO();
                        outVO.setValue(entry.getValue());
                        outVO.setType(entry.getKey());
                        outVO.setId(entry.getKey());
                        outVO.setDesc(entry.getKey() + Constants.COLON + entry.getValue());
                        if (!result.contains(outVO)) {
                            result.add(outVO);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("UMP interface error.Method:getFilters", e);
        }
        return result;
    }
}
